// ==========================================================
// FreeImage 3 .NET wrapper
// Original FreeImage 3 functions and .NET compatible derived functions
//
// Design and implementation by
// - Jean-Philippe Goerke (jpgoerke@users.sourceforge.net)
// - Carsten Klein (cklein05@users.sourceforge.net)
//
// Contributors:
// - David Boland (davidboland@vodafone.ie)
//
// Main reference : MSDN Knowledge Base
//
// This file is part of FreeImage 3
//
// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
// THIS DISCLAIMER.
//
// Use at your own risk!
// ==========================================================

// ==========================================================
// CVS
// $Revision: 1.7 $
// $Date: 2009/02/27 16:34:59 $
// $Id: ImageMetadata.cs,v 1.7 2009/02/27 16:34:59 cklein05 Exp $
// ==========================================================

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Diagnostics;

namespace FreeImageAPI.Metadata
{
    /// <summary>
    /// Class handling metadata of a FreeImage bitmap.
    /// </summary>
    public class ImageMetadata : IEnumerable, IComparable, IComparable<ImageMetadata>
    {
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly List<MetadataModel> data;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private readonly FIBITMAP dib;
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        private bool hideEmptyModels;

        /// <summary>
        /// Initializes a new instance based on the specified <see cref="FIBITMAP"/>,
        /// showing all known models.
        /// </summary>
        /// <param name="dib">Handle to a FreeImage bitmap.</param>
        public ImageMetadata(FIBITMAP dib) : this(dib, false) { }

        /// <summary>
        /// Initializes a new instance based on the specified <see cref="FIBITMAP"/>,
        /// showing or hiding empty models.
        /// </summary>
        /// <param name="dib">Handle to a FreeImage bitmap.</param>
        /// <param name="hideEmptyModels">When <b>true</b>, empty metadata models
        /// will be hidden until a tag to this model is added.</param>
        public ImageMetadata(FIBITMAP dib, bool hideEmptyModels)
        {
            if (dib.IsNull) throw new ArgumentNullException("dib");
            data = new List<MetadataModel>(FreeImage.FREE_IMAGE_MDMODELS.Length);
            this.dib = dib;
            this.hideEmptyModels = hideEmptyModels;

            data.Add(new MDM_ANIMATION(dib));
            data.Add(new MDM_COMMENTS(dib));
            data.Add(new MDM_CUSTOM(dib));
            data.Add(new MDM_EXIF_EXIF(dib));
            data.Add(new MDM_EXIF_GPS(dib));
            data.Add(new MDM_INTEROP(dib));
            data.Add(new MDM_EXIF_MAIN(dib));
            data.Add(new MDM_MAKERNOTE(dib));
            data.Add(new MDM_GEOTIFF(dib));
            data.Add(new MDM_IPTC(dib));
            data.Add(new MDM_NODATA(dib));
            data.Add(new MDM_XMP(dib));
        }

        /// <summary>
        /// Gets or sets the <see cref="MetadataModel"/> of the specified type.
        /// <para>In case the getter returns <c>null</c> the model is not contained
        /// by the list.</para>
        /// <para><c>null</c> can be used calling the setter to destroy the model.</para>
        /// </summary>
        /// <param name="model">Type of the model.</param>
        /// <returns>The <see cref="FreeImageAPI.Metadata.MetadataModel"/> object of the specified type.</returns>
        public MetadataModel this[FREE_IMAGE_MDMODEL model]
        {
            get
            {
                for (int i = 0; i < data.Count; i++)
                {
                    if (data[i].Model == model)
                    {
                        if (!data[i].Exists && hideEmptyModels)
                        {
                            return null;
                        }
                        return data[i];
                    }
                }
                return null;
            }
        }

        /// <summary>
        /// Gets or sets the <see cref="FreeImageAPI.Metadata.MetadataModel"/> at the specified index.
        /// <para>In case the getter returns <c>null</c> the model is not contained
        /// by the list.</para>
        /// <para><c>null</c> can be used calling the setter to destroy the model.</para>
        /// </summary>
        /// <param name="index">Index of the <see cref="FreeImageAPI.Metadata.MetadataModel"/> within
        /// this instance.</param>
        /// <returns>The <see cref="FreeImageAPI.Metadata.MetadataModel"/>
        /// object at the specified index.</returns>
        public MetadataModel this[int index]
        {
            get
            {
                if (index < 0 || index >= data.Count)
                {
                    throw new ArgumentOutOfRangeException("index");
                }
                return (hideEmptyModels && !data[index].Exists) ? null : data[index];
            }
        }

        /// <summary>
        /// Returns a list of all visible
        /// <see cref="FreeImageAPI.Metadata.MetadataModel">MetadataModels</see>.
        /// </summary>
        public List<MetadataModel> List
        {
            get
            {
                if (hideEmptyModels)
                {
                    List<MetadataModel> result = new List<MetadataModel>();
                    for (int i = 0; i < data.Count; i++)
                    {
                        if (data[i].Exists)
                        {
                            result.Add(data[i]);
                        }
                    }
                    return result;
                }
                else
                {
                    return data;
                }
            }
        }

        /// <summary>
        /// Adds new tag to the bitmap or updates its value in case it already exists.
        /// <see cref="FreeImageAPI.Metadata.MetadataTag.Key"/> will be used as key.
        /// </summary>
        /// <param name="tag">The tag to add or update.</param>
        /// <returns>Returns true on success, false on failure.</returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="tag"/> is null.</exception>
        public bool AddTag(MetadataTag tag)
        {
            for (int i = 0; i < data.Count; i++)
            {
                if (tag.Model == data[i].Model)
                {
                    return data[i].AddTag(tag);
                }
            }
            return false;
        }

        /// <summary>
        /// Returns the number of visible
        /// <see cref="FreeImageAPI.Metadata.MetadataModel">MetadataModels</see>.
        /// </summary>
        public int Count
        {
            get
            {
                if (hideEmptyModels)
                {
                    int count = 0;
                    for (int i = 0; i < data.Count; i++)
                    {
                        if (data[i].Exists)
                        {
                            count++;
                        }
                    }
                    return count;
                }
                else
                {
                    return data.Count;
                }
            }
        }

        /// <summary>
        /// Gets or sets whether empty
        /// <see cref="FreeImageAPI.Metadata.MetadataModel">MetadataModels</see> are hidden.
        /// </summary>
        public bool HideEmptyModels
        {
            get
            {
                return hideEmptyModels;
            }
            set
            {
                hideEmptyModels = value;
            }
        }

        /// <summary>
        /// Retrieves an object that can iterate through the individual
        /// <see cref="FreeImageAPI.Metadata.MetadataModel">MetadataModels</see>
        /// in this <see cref="ImageMetadata"/>.
        /// </summary>
        /// <returns>An <see cref="IEnumerator"/> for this <see cref="ImageMetadata"/>.</returns>
        public IEnumerator GetEnumerator()
        {
            if (hideEmptyModels)
            {
                List<MetadataModel> tempList = new List<MetadataModel>(data.Count);
                for (int i = 0; i < data.Count; i++)
                {
                    if (data[i].Exists)
                    {
                        tempList.Add(data[i]);
                    }
                }
                return tempList.GetEnumerator();
            }
            else
            {
                return data.GetEnumerator();
            }
        }

        /// <summary>
        /// Compares this instance with a specified <see cref="Object"/>.
        /// </summary>
        /// <param name="obj">An object to compare with this instance.</param>
        /// <returns>A 32-bit signed integer indicating the lexical relationship between the two comparands.</returns>
        /// <exception cref="ArgumentException"><paramref name="obj"/> is not a <see cref="ImageMetadata"/>.</exception>
        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            if (!(obj is ImageMetadata))
            {
                throw new ArgumentException("obj");
            }
            return CompareTo((ImageMetadata)obj);
        }

        /// <summary>
        /// Compares this instance with a specified <see cref="ImageMetadata"/> object.
        /// </summary>
        /// <param name="other">A <see cref="ImageMetadata"/> to compare.</param>
        /// <returns>A signed number indicating the relative values of this instance
        /// and <paramref name="other"/>.</returns>
        public int CompareTo(ImageMetadata other)
        {
            return this.dib.CompareTo(other.dib);
        }
    }
}